﻿//How do I convert an image with 10 or 12 bit color to 8 bit color?
//The most direct approach is to use the "lookup" operator to map your 10 or 12 bit data to 8-bit values. If you just want to do simple range conversion, then, for 12 bit data the lookup table's data can be created by:
//byte[] blut = new byte[4096];
//for (int i=0; i < 4096; i++) {
//    blut[i] = (byte)(i >> 4);
//}
//A refinement of this is to apply a linear "contrast stretch" (really a brightness adjustment) by mapping the min and max values in the 12 bit image to 0 and 255 in the 8 bit image. (Note that we're cheating here and stuffing values we're interpreting as unsigned into Java's signed byte type).
//You can determine the min/max using Extrema operator. You will normally want to choose the min of all bands as the minimum, and likewise for the max. Doing it band by band will cause color shifts. Of course, if the imagery is false color, you may not care.

//You then create the table by:

//double scale = 255.0/(float)(max-min);
//for (int i = min; i <= max; i++) {
//   blut[i] = (byte)((i - min)*scale);
//}

//// Clamp any input values outside
//// min/max range, just in case
//for (int i = 0; i < min; i++) {
//   blut[i] = 0;
//}

//for (int i = max; i < 4096; i++) {
//    blut[i] = (byte)255;
//}
//You may find that these approaches sacrifice too much detail at the low radiance end of the scale. Frequently a non-linear transformation is needed, usually somewhere between square-root and cube-root.

//for (int i = 0; i < 4096; i++) {
//    double f = i*(1.0/4095.0);
//    // Take the square root
//    f = Math.pow(f, 0.5);
//    // Scale to 8 bits and round
//    blut[i] = (byte)(f*255.0 + 0.5);
//}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using LowLevelGraphics.Histograms;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Stretches the gray contrast 
    /// </summary>
    public class ContrastStretch : Contrast
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ContrastStretch"/> class.
        /// </summary>
        public ContrastStretch()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ContrastStretch"/> class.
        /// </summary>
        /// <param name="_contrastType">Type of the _contrast.</param>
        public ContrastStretch(CONTRAST_TYPE _contrastType)
        {
            this.m_CONTRAST_TYPE = _contrastType;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ContrastStretch"/> class.
        /// </summary>
        /// <param name="_contrastStretch">The _contrast stretch.</param>
        internal ContrastStretch(ContrastStretch _contrastStretch)
        {
            this.m_CONTRAST_TYPE = _contrastStretch.m_CONTRAST_TYPE;
        }

        /// <summary>
        /// Gets or sets the type.
        // </summary>
        /// <value>The type.</value>
        public CONTRAST_TYPE Type
        {
            get { return m_CONTRAST_TYPE; }
            set { m_CONTRAST_TYPE = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bmpimg = _bitmap;
            BitmapData data = bmpimg.BitmapData; //bmpimg.LockBits(new Rectangle(0, 0, bmpimg.Width, bmpimg.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int nBytesPerPixelCount = bmpimg.BytesPerPixelCount;
            int nHeight = data.Height;
            int nWidth = data.Width;

            //TODO: RMS CONTRAST, ContrastStretch, TEST
            if (m_CONTRAST_TYPE == CONTRAST_TYPE.RMS)
            {
                return RMS(_bitmap, bmpimg, nHeight, nWidth);
            }

            unsafe
            {
                byte* ptr = (byte*)data.Scan0;

                float fMin = 255;
                float fMax = 0;

                int nRemain = data.Stride - nWidth * nBytesPerPixelCount; //data.Stride - nWidth * 3;

                for (int i = 0; i < nHeight; i++)
                {
                    for (int j = 0; j < nWidth; j++)
                    {
                        if (fMin > ptr[0])
                        {
                            fMin = ptr[0];
                        }

                        if (fMax < ptr[0])
                        {
                            fMax = ptr[0];
                        }

                        ptr += nBytesPerPixelCount;
                    }
                    ptr += nRemain;
                }

                ptr = (byte*)data.Scan0;

                for (int i = 0; i < nHeight; i++)
                {
                    for (int j = 0; j < nWidth; j++)
                    {
                        if (m_CONTRAST_TYPE == CONTRAST_TYPE.MICHELSON)
                        {
                            ptr[0] = ptr[1] = ptr[2] = (byte)((ptr[0] - fMin) * (255 / (fMax - fMin)));
                        }
                        else if (m_CONTRAST_TYPE == CONTRAST_TYPE.WEBER)
                        {
                            ptr[0] = ptr[1] = ptr[2] = (byte)(fMax / fMin - 1);
                        }
                        else if (m_CONTRAST_TYPE == CONTRAST_TYPE.DR4G0N76)
                        {
                            int nA = ColorFunctions.ToIntRGB(ptr[2], ptr[1], ptr[0]);
                            if (nA < 0x7FFFFF)
                            {
                                nA /= 2;
                            }
                            else
                            {
                                nA *= 2;
                                if (nA > 0xFFFFFF)
                                {
                                    nA = 0xFFFFFF;
                                }
                            }
                            Color color = Color.FromArgb(nA);
                            ptr[2] = color.R;
                            ptr[1] = color.G;
                            ptr[0] = color.B;
                        }
                        ptr += nBytesPerPixelCount;
                    }
                    ptr += nRemain;
                }
            }

            return _bitmap;
        }

        /// <summary>
        /// RMSs the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="bmpimg">The bmpimg.</param>
        /// <param name="nHeight">Height of the n.</param>
        /// <param name="nWidth">Width of the n.</param>
        /// <returns></returns>
        protected UnsafeBitmap RMS(UnsafeBitmap _bitmap, UnsafeBitmap bmpimg, int nHeight, int nWidth)
        {
            int nPixelCount = nWidth * nHeight;
            Statistics histogram = new Statistics(_bitmap);
            histogram.SortBy(PROCESSING_MODE.Color);
            int nMeanRGB = 0;
            foreach (KeyValuePair<Color, int> keyValuePair in histogram.ColorList)
            {
                nMeanRGB += keyValuePair.Key.ToArgb();
            }
            nMeanRGB /= histogram.ColorList.Count;

            int x = 0;
            int y = 0;

            Color color = Color.Transparent;
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    int nARGB = (int)_bitmap.GetPixel(x, y).ToArgb();
                    nARGB = (int)Math.Sqrt((nARGB - nMeanRGB) * (nARGB - nMeanRGB) / nPixelCount);
                    _bitmap.SetPixel(x, y, Color.FromArgb(nARGB));
                }
            }
            return bmpimg;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ContrastStretch(this);
        }
    }
}
